-- SkyActivityM
-- Created by chendh May/7/2016
-- 天空活动模块

module("SkyActivityM", package.seeall);

local PURE_FILE = "SkyActivityM";

-- 配置表
local skyActivityTable          = {};
local skyActivityBonusTable     = {};
local skyExchagneTable          = {};
local skyActivityCombatTable    = {};
local dungeonEggsTable          = {};
local failEggsTable             = {};
local killEggsTable             = {};

-- 正在进行中的活动
local currentActivities = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 天空活动配置
    skyActivityTable = {};
    local activity_CSV = get_sky_activity_CSV();
    for i = 1, #activity_CSV do
        local id = activity_CSV[i].id;
        skyActivityTable[id] = activity_CSV[i];
    end
    activity_CSV = nil;

    -- 清空列表
    currentActivities = {};

    -- 天空活动奖励配置
    skyActivityBonusTable = {};
    local bonus_CSV = get_sky_activity_bonus_CSV();
    for i = 1, #bonus_CSV do
        local id = bonus_CSV[i].id;
        skyActivityBonusTable[id] = bonus_CSV[i];
    end
    bonus_CSV = nil;

    -- 天空活动兑换配置
    skyExchagneTable = {};
    local exchange_CSV = get_sky_activity_exchange_CSV();
    for i = 1, #exchange_CSV do
        local id = exchange_CSV[i].id;
        skyExchagneTable[id] = exchange_CSV[i];
    end
    exchange_CSV = nil;

    -- 天空活动战斗配置
    skyActivityCombatTable = {};
    local combat_CSV = get_sky_activity_combat_CSV();
    for i = 1, #combat_CSV do
        local id = combat_CSV[i].id;
        skyActivityCombatTable[id] = combat_CSV[i];
    end
    combat_CSV = nil;

    -- 天空活动地牢彩蛋配置
    dungeonEggsTable = {};
    local dungeonEggs_CSV = get_sky_activity_dungeon_eggs_CSV();
    for i = 1, #dungeonEggs_CSV do
        local id = dungeonEggs_CSV[i].id;
        dungeonEggsTable[id] = dungeonEggs_CSV[i];
    end
    dungeonEggs_CSV = nil;

    -- 天空活动失败彩蛋配置
    failEggsTable = {};
    local failEggs_CSV = get_sky_activity_fail_eggs_CSV();
    for i = 1, #failEggs_CSV do
        local id = failEggs_CSV[i].id;
        failEggsTable[id] = failEggs_CSV[i];
    end
    failEggs_CSV = nil;

    -- 天空活动击杀彩蛋配置
    killEggsTable = {};
    local killEggs_CSV = get_sky_activity_kill_eggs_CSV();
    for i = 1, #killEggs_CSV do
        local id = killEggs_CSV[i].id;
        killEggsTable[id] = killEggs_CSV[i];
    end
    killEggs_CSV = nil;
end

-- 模块初始化
function init()
    -- 载入活动配置表
    loadCsv();
end

-- 检索活动配置
function query(id, path)
    if not skyActivityTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return skyActivityTable[id];
    end

    return skyActivityTable[id][path];
end

-- 检索活动奖励配置
function queryBonus(id, path)
    if not skyActivityBonusTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return skyActivityBonusTable[id];
    end

    return skyActivityBonusTable[id][path];
end

-- 检索活动兑换配置
function queryExchange(id, path)
    if not skyExchagneTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return skyExchagneTable[id];
    end

    return skyExchagneTable[id][path];
end

-- 检索活动战斗配置
function queryCombat(id, path)
    if not skyActivityCombatTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return skyActivityCombatTable[id];
    end

    return skyActivityCombatTable[id][path];
end

-- 检索活动地牢彩蛋配置
function queryDungeonEggs(id, path)
    if not dungeonEggsTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return dungeonEggsTable[id];
    end

    return dungeonEggsTable[id][path];
end

-- 检索活动失败彩蛋配置
function queryFailEggs(id, path)
    if not failEggsTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return failEggsTable[id];
    end

    return failEggsTable[id][path];
end

-- 检索活动击杀配置
function queryKillEggs(id, path)
    if not killEggsTable[id] then
        return nil;
    end

    if path == nil or path == "" then
        return killEggsTable[id];
    end

    return killEggsTable[id][path];
end

-- 检测指定的活动现在是否正在开放
function checkIsOpen(id)
    local activityInfo = ActivityM.getActivityInfo(id);

    -- 如果没有当前活动信息，那么说明没有活动，直接返回FALSE
    if type(activityInfo) ~= "table" then
        return false;
    end

    -- 活动必须是开放状态才能参加
    if ActivityM.getActivityStatus(activityInfo) ~= ACTIVITY_STATUS_START then
        return false;
    end

    -- 判定通过
    return true;
end

-- 检测指定的活动现在是否正在运行
function checkIsRunning(id)
    local activityInfo = ActivityM.getActivityInfo(id);

    -- 如果没有当前活动信息，那么说明没有活动，直接返回FALSE
    if type(activityInfo) ~= "table" then
        return false;
    end

    -- 活动的开放和结束状态认为活动正在运行
    if ActivityM.getActivityStatus(activityInfo) == ACTIVITY_STATUS_START or
        ActivityM.getActivityStatus(activityInfo) == ACTIVITY_STATUS_END then
        return true;
    end

    return false;
end

-- 获取活动奖励列表
function getActivityBonusList(activityId)
    -- 如果活动结束了，则返回空table
    if not checkIsRunning(activityId) or
        ActivityM.getRemainRunningTime(activityId) <= 0 then
        return {};
    end

    local activityInfo = ActivityM.getActivityInfo(activityId);
    local result = {};
    for id, bonusInfo in pairs(skyActivityBonusTable) do
        repeat
            if bonusInfo["activity"] ~= activityId then
                break;
            end

            -- 如果是公式的话，那么就计算一下
            if type(bonusInfo["bonus"]) == "string" then
                bonusInfo["bonus"] = FormulaM.invoke(bonusInfo["bonus"], bonusInfo, activityInfo["activity_times"]);
            end

            table.insert(result, bonusInfo);
        until true;
    end

    local function sortBonus(info1, info2)
        return info1["score"] > info2["score"];
    end

    table.sort(result, sortBonus);

    return result;
end

-- 计算复活消耗
function calcReviveCost(combatData)
    local reviveTimes = combatData["revive"] or 0;
    local gemCost = FormulaM.invoke("CALC_SC_ACTIVITY_REVIVE_GEM_COST", reviveTimes);

    return { 2, "gem", gemCost, };
end

-- 获取玩家的活动数据
function getActivityData(id)
    local activityData = ME.user.dbase:query("sky_activity", {});

    return activityData[id] or {};
end

-- 计算兑换消耗的徽章数量
function calcExchangeCostNum(activityId, id)
    -- 如果活动结束了，则返回0
    if not checkIsRunning(activityId) or
        ActivityM.getRemainRunningTime(activityId) <= 0 then
        return 0;
    end

    local exchagneConfig = queryExchange(id);
    local activityInfo  = ActivityM.getActivityInfo(activityId) or {};
    local times         = activityInfo["activity_times"] or 0;

    if not exchagneConfig then
        return 0;
    end

    -- 必须已经开始出现
    if exchagneConfig["start_show"] > times then
        return 0;
    end

    -- 计算下消耗的勋章数量
    local cost_num;
    local i = times - exchagneConfig["start_show"];
    if i >= #exchagneConfig["price"] then
        cost_num = exchagneConfig["price"][#exchagneConfig["price"]];
    else
        cost_num = exchagneConfig["price"][i + 1];
    end

    return cost_num;
end

-- 获取活动兑换列表
function getActivityExchangeList(activityId)
    -- 如果活动结束了，则返回空列表
    if not checkIsRunning(activityId) or
        ActivityM.getRemainRunningTime(activityId) <= 0 then
        return {};
    end

    local result        = {};
    local activityData  = getActivityData(activityId);
    local exchagneData  = activityData["exchange_data"] or {};
    local activityInfo  = ActivityM.getActivityInfo(activityId) or {};
    local times         = activityInfo["activity_times"] or 0;
    local medalClassId  = query(activityId, "medal_class_id");

    for id, info in pairs(skyExchagneTable) do
        repeat
            -- 必须属于指定的活动
            if type(info["activity"]) == "number" and
                info["activity"] ~= activityId then
                break;
            end

            if type(info["activity"]) == "table" and
                table.indexOf(info["activity"], activityId) <= 0 then
                break;
            end

            -- 必须已经开始出现
            if info["start_show"] > times then
                break;
            end

            -- 数量限制必须未到达
            --[[if type(info["limit"]) == "number" and
                type(exchagneData[id]) == "number" and
                exchagneData[id] >= info["limit"] then
                break;
            end

            -- 历史数量限制必须未到达
            if type(info["history_limit"]) == "number" and
                type(info["history_limit_id"]) == "number" and
                BuffDataM.query("sky_activity_exchange",info["history_limit_id"] ) >= info["limit"] then
                break;
            end]]

            -- 复制下，避免修改到源数据
            local data = table.copy(info);

            -- 计算下剩余可兑换数量，默认是-1代表无限制
            local count = -1;
            if type(info["limit"]) == "number" and
                type(info["history_limit"]) == "number" then
                count = math.min(info["limit"] - (exchagneData[id] or 0),
                    info["history_limit"] - BuffDataM.query("sky_activity_exchange",info["history_limit_id"]));
            elseif type(info["limit"]) == "number" then
                count = info["limit"] - (exchagneData[id] or 0);
            elseif type(info["history_limit"]) == "number" then
                count = info["history_limit"] - BuffDataM.query("sky_activity_exchange",info["history_limit_id"]);
            end
            data["count"] = count;

            -- 计算下消耗的勋章数量
            local cost_num = calcExchangeCostNum(activityId, id);
            if cost_num <= 0 then
                break;
            end

            data["price"] = { 1, medalClassId, cost_num };

            table.insert(result, data);
        until true;
    end

    return result;
end

-- 能否兑换
function canExchange(activityId, goodsId, num)
    local activityData  = getActivityData(activityId);
    local exchagneData  = activityData["exchange_data"] or {};
    local activityInfo  = ActivityM.getActivityInfo(activityId) or {};
    local times         = activityInfo["activity_times"] or 0;
    local info          = queryExchange(goodsId);

    -- 计算下剩余可兑换数量，默认是-1代表无限制
    local count = -1;
    if type(info["limit"]) == "number" and
        type(info["history_limit"]) == "number" then
        count = math.min(info["limit"] - (exchagneData[goodsId] or 0),
            info["history_limit"] - BuffDataM.query("sky_activity_exchange",info["history_limit_id"]));
    elseif type(info["limit"]) == "number" then
        count = info["limit"] - (exchagneData[goodsId] or 0);
    elseif type(info["history_limit"]) == "number" then
        count = info["history_limit"] - BuffDataM.query("sky_activity_exchange",info["history_limit_id"]);
    end

    if count == 0 then
        return "sold_out";
    end

    -- 计算下消耗的勋章数量
    local cost_num = calcExchangeCostNum(activityId, goodsId);
    local medalClassId  = query(activityId, "medal_class_id");
    local curMedalNum = ItemM.getAmount(ME.user, medalClassId);
    if curMedalNum < cost_num * num then
        return "medal_lack";
    end

    return true;
end

-- 获取活动战斗状态
function getCombatStatus(activityId, combatId)
    local activityData = getActivityData(activityId);
    local combatInfo = activityData["combat"] or {};
    local data = combatInfo[combatId] or {};

    if data["is_die"] then
        return "is_die";
    else
        local condition = queryCombat(combatId, "condition");
        if type(condition) == "table" and #condition > 0 then
            for _, id in ipairs(condition) do
                if getCombatStatus(activityId, id) ~= "is_die" then
                    return "lock";
                end
            end
        end

        return "open";
    end
end

-- 判断能否发起战斗
function canStartCombat(activityId, combatId)
    -- 如果活动当前不是open状态，则不能发起战斗
    local status = ActivityM.getActivityStatus(ActivityM.getActivityInfo(activityId));
    if status ~= ACTIVITY_STATUS_START then
        return false;
    end

    -- 如果是隐藏战斗，那么必须能够看到隐藏BOSS，否则不能发起战斗
    local hideCombat = SkyActivityM.query(activityId, "hide_combat");
    if combatId == hideCombat and not canViewHideBoss(activityId) then
        return false;
    end

    -- 如果当前战斗的状态不是open，那么无法发起战斗
    local combatStatus = SkyActivityM.getCombatStatus(activityId, combatId);
    if combatStatus ~= "open" then
        return false;
    end

    return true;
end

-- 获取最大参与次数
function getMaxTimes(activityId)
    return 9;
end

-- 是否可以看见隐藏BOSS
function canViewHideBoss(activityId)
    -- 必须配置了隐藏BOSS
    local hideCombat = query(activityId, "hide_combat");
    if type(hideCombat) ~= "number" or hideCombat <= 0 then
        return false;
    end

    -- 必须所有的BOSS都击杀了
    local combatList = query(activityId, "combat");
    for _, combatId in ipairs(combatList) do
        if getCombatStatus(activityId, combatId) ~= "is_die" then
            return false;
        end
    end

    -- 必须是最后一天
    local activityInfo = ActivityM.getActivityInfo(activityId);
    local remainTime = ActivityM.getRemainTime(activityId);
    if remainTime < 0 or activityInfo["end_time"] > activityInfo["daily_reset_time"] then
        return false;
    end

    return true;
end

-- 获取天空活动招募列表
function getSkyActivityRecruitList(activityId)
    local ret = {};
    local activityData = getActivityData(activityId);
    local times = activityData["activity_times"] or 0;
    local list = FormulaM.invoke("CALC_SKY_ACTIVITY_RECRUIT_LIST", times);

    for _, petId in ipairs(list) do
        if not ME.user.pets[petId] then
            table.insert(ret, petId);
        end
    end

    return ret;
end

-- 获取天空活动勋章的class_id列表
function getSkyAcitivityMedalList()
    local ret = {};

    for _, activityInfo in pairs(skyActivityTable) do
        if type(activityInfo["medal_class_id"]) == "number" then
            table.insert(ret, activityInfo["medal_class_id"]);
        end
    end

    return ret;
end